
#include "LabviewConverter.hh"

#include <marlin/ProcessorMgr.h>
#include "marlin/Exceptions.h"

#include <EVENT/LCEvent.h>
#include <EVENT/LCRunHeader.h>

#include <UTIL/LCTOOLS.h>
#include <fstream>
#include <iostream>
#include <iomanip>

#include "LConverter.hh"
#include "LabviewBlock.hh"

#include "E4DMapper.hh"
#include "HBUMapper.hh"
#include "UTIL/LCTime.h"


#include <time.h>

using namespace std;
using namespace lcio;
using namespace CALICE;


namespace marlin{


  LabviewConverter aLabviewConverter ;

  
  LabviewConverter::LabviewConverter() : DataSourceProcessor("LabviewConverter")
  {
    
    _description = "Reads the ascii ahcal labview raw data files. Make sure to not specify any LCIOInputFiles in the steering in order to read the Calice native raw data files ." ;
    
    registerProcessorParameter( "Data" , 
				"Initial String to the ahcal data file. i.e. 'Run'"  ,
				_data ,
				std::string("run270001.txt") ) ;
  
    registerProcessorParameter ( "Runnumber", "Runnumber", _runNumber, 270000);    

    registerProcessorParameter ( "HBUnumber",
				 "HBU number:  HBUVI: 6; HBUVI: 7, HBUVIII: 8, HBUIX: 9",
				 _HBUNumber,
				 6);    

    registerProcessorParameter( "localMappingFile" , 
				"Initial String to the ahcal mapping file."  ,
				_mapping ,
				std::string("map.txt") ) ;
 
    registerProcessorParameter( "useDBMapping" , 
				"bool flag to use the ahcal HBU mapping from CALICE database."  ,
				_useDBMapping ,
				true ) ;
   
    registerProcessorParameter( "DBinit" , 
				"Initial string to the CALICE database."  ,
				_dbinit ,
				std::string("flccaldb02.desy.de:calice:caliceon:Delice.1:3306") ) ;


    registerProcessorParameter ( "DBfolder", 
				 "/test_lu/E4DMapper", 
				 _dbfolder, 
				 std::string("/test_lu/E4DMapper") );    
   
    registerProcessorParameter ( "DBfoldertag", 
				 "Use the HEAD tag", 
				 _tag, 
				 std::string("") );    
    
    registerProcessorParameter ( "DBtimeStamp", 
				 "Read the HBU Mapping from the CALICE database at this timeStamp", 
				 _timeStamp, 
				 std::string("4. Sep 2012 11:10:35") );
    //				 std::string("2012-Sep-4-11:20:30") );    
    //				 std::string("1343779200000000001LL") );    
    

  }
  
  LabviewConverter*  LabviewConverter::newProcessor() { 
    return new LabviewConverter ;
  }
  
  void LabviewConverter::init() {    
    // printParameters() ;    

    //_lConverter.init();
  }

    
   void LabviewConverter::readDataSource( int numEvents ) {
     
     ifstream file_to_read;
     file_to_read.open( _data.c_str() );

     //ifstream map_to_read;
     //map_to_read.open( _mapping.c_str() );


     int rn = _runNumber;

     LCRunHeaderImpl* runHdr = new LCRunHeaderImpl ; 
     runHdr->setRunNumber( rn ) ;

     string detName("ahcal_next_generation")  ;
     runHdr->setDetectorName( detName ) ;
      
     stringstream description ; 
     description << " run: " << rn <<" is the raw data from Labview !" ;
     runHdr->setDescription( description.str()  ) ;

     ProcessorMgr::instance()->processRunHeader( runHdr  ) ;

     int read[12];
     //unsigned int mapping[6];

     int currentCycleNr = 1;
     int lastCycleNr = 1;
     bool CycleNrChanged = false;
     
     int LcioEventNr = 0;
     
     LConverter _ldata;
     
     std::string str;
     
     E4DMapper e4dmapping; 

     if (_useDBMapping) {

       // ********************************
       // read from databse 
       // ********************************
      
       //UTIL::LCTime E4DMapperTime(2012,9,30,00,00,00); //1348981200, 09.30.2012 

       /*
	 std::string E4DMapperTime = "Di, 4. Sep 2012 11:10:35";
	 struct tm tm;
	 long timeStamp = 0; //13437792000/00000000;
	 if ( strptime(E4DMapperTime.c_str(), "%d. %b %Y %H:%M:%S", &tm) != NULL ){
	 time_t readE4DMapperTime;
	 readE4DMapperTime = mktime(&tm);
	 timeStamp = (long)readE4DMapperTime;
	 m_out(MESSAGE) <<" And the Unix UTC Timestamp  is "  << timeStamp <<endl;
	 }
       */

       struct tm tm;
       time_t readE4DMapperTime;

       //if ( strptime(_timeStamp.c_str(), "%Y-%b-%d-%H:%M:%S", &tm) != NULL ){
       if ( strptime(_timeStamp.c_str(), "%d. %b %Y %H:%M:%S", &tm) != NULL ){
	 readE4DMapperTime = mktime(&tm);
       } else {
	 m_out(ERROR) <<" Wrong time stamp"<<std::endl;
	 return;
       }
       lccd::LCCDTimeStamp E4DMapperTime = static_cast<lccd::LCCDTimeStamp>(readE4DMapperTime)*1000000000LL;
	 
	 //m_out(MESSAGE) <<" And the Unix UTC Timestamp  is "  << timeStamp <<endl;
       
       //lcio::long64  E4DMapperTime();
       //lccd::LCCDTimeStamp E4DMapperTime(1343779200000000001LL);// = _timeStamp;
       //std::cout<<"timeStamp: " << E4DMapperTime << std::endl;
       m_out(MESSAGE)<<"The HBUMapping timeStamp: " << E4DMapperTime << std::endl;

       lccd::DBInterface dbi( _dbinit, _dbfolder );
       //EVENT::LCCollection *col = dbi.findCollection( E4DMapperTime.timeStamp(), tag );
       EVENT::LCCollection *col = dbi.findCollection( E4DMapperTime, _tag );
      
       if (col == NULL)
	 {
	   cout<<"Could not find collection for time "<<E4DMapperTime<<endl;
	   exit(1);
	 }

       for ( int i = 0; i != col->getNumberOfElements(); ++i ) 
	 {     
	   CALICE::HBUMapper hbum( col->getElementAt( i ) );
	   e4dmapping.createMapbyMCCIJK(_HBUNumber,
					hbum.getChip(),
					hbum.getChannel(),
					hbum.getI(),
					hbum.getJ(),
					hbum.getK());
	 }
     } else {

       // ********************************
       // read from ascii file map.txt
       // ********************************
       
       ifstream map_to_read;
       map_to_read.open( _mapping.c_str() );
       
       int mapping[6];

       while (!map_to_read.eof()) {
	 for( int i = 0; i<6; i++) { mapping[i] = 0; }
	 std::getline(map_to_read, str);
	 if(str[0] == '#') continue; //for comments
	
	 map_to_read >>mapping[0]>>mapping[1]>>mapping[2]>>mapping[3]>>mapping[4]>>mapping[5];
	 
	 e4dmapping.createMapbyMCCIJK(mapping[0],mapping[1],mapping[2],mapping[3],mapping[4],mapping[5]);

       }
     
     }



     e4dmapping.printMap();
     
     size_t found;
     string str2 ("Date");

     int iEvtTime = 0;

     //Processing data
     while (!file_to_read.eof()) {

       for( int i = 0; i<12; i++) { read[i] = -1;}

       std::getline(file_to_read, str);
       //if(str[0] == '#') continue; //for comments

       if(str[0] == '#'){
	 
	 found = str.find(str2);
	 if (found!=string::npos) {
	   str.erase (0,20);
	   //size_t found2 = str.find(".");
	   //if (found2!=string::npos) {
	   //  str.erase (found2,1);
	   //}
	   m_out(MESSAGE) <<" These events have been collected on " << str <<endl; 

	   //convert this string into timestamp
	   struct tm tm;
	   //if ( strptime(str.c_str(), "%d %m %Y %H:%M:%S", &tm) != NULL ){
	   if ( strptime(str.c_str(), "%d. %b %Y %H:%M:%S", &tm) != NULL ){
	     time_t evtTimeDAQ;
	     evtTimeDAQ = mktime(&tm);
	     iEvtTime = (int)evtTimeDAQ;
	     m_out(MESSAGE) <<" And the Unix UTC Timestamp  is "  << evtTimeDAQ <<endl;
	   }
	   else {
	     cout <<"Something wrong for the Timestamp!"<<endl;
	   }
	   
	 }

	 continue; //for comments
       }
 
       stringstream(str)>>read[0]>>read[1]>>read[2]>>read[3]>>read[4]>>read[5]>>read[6]>>read[7]>>read[8]>>read[9]>>read[10]>>read[11];

       currentCycleNr =read[1];

       if( lastCycleNr != currentCycleNr )
	 {

	   CycleNrChanged = true;
	   lastCycleNr = currentCycleNr;

	     if( CycleNrChanged )
	       {
		 m_out(DEBUG0) <<" _ldata.size(): "<<_ldata.GetBunchXID().size() <<std::endl;

		 _ldata.reverse();
		 //Splite event according bunchXID, count event number.

		 int TotalRcdNr = _ldata.GetEvtNr().size();
		 int MaxRcdEvtNr = 0;//_ldata.GetEvtNr().at(TotalRcdNr - 1) + 1; //0,1,2 is 3 events
		 for( unsigned int i=0; i<_ldata.GetBunchXID().size(); i++) {
		   if (MaxRcdEvtNr < _ldata.GetEvtNr().at(i) ) MaxRcdEvtNr = _ldata.GetEvtNr().at(i) + 1;
		 }

		 m_out(DEBUG0) << "TotalRcdNr: " <<TotalRcdNr
			       << "     MaxRcdEvtNr: " <<MaxRcdEvtNr
			       << std::endl;
		 
		 //int currentBunchXID = _ldata.GetBunchXID().at(0);
		 //int nextBunchXID = _ldata.GetBunchXID().at(0);

		 for( int iEv = 0; iEv < MaxRcdEvtNr; iEv++ )
		   {

		     LcioEventNr++;
		     //m_out(MESSAGE)<< " ===== LabviewConverter processing Event "<< LcioEventNr <<"  ======" <<std::endl;

		     LCEventImpl*  evt = new LCEventImpl() ;
	
	
		     evt->setRunNumber(  rn   ) ;
		     evt->setEventNumber( LcioEventNr ) ;
		     LCTime evttime( iEvtTime ) ;
		     evt->setTimeStamp( evttime.timeStamp()  ) ;

		     

		     LCCollectionVec *col = new LCCollectionVec( LCIO::LCGENERICOBJECT );

		     int size = _ldata.GetBunchXID().size() -1;
		     int currentBunchXID = _ldata.GetBunchXID().at( size );

		     //for( unsigned int i=0; i<_ldata.GetBunchXID().size(); i++) {
		     for( unsigned int i = size; i > 0;  i--) {

		       //if ( _ldata.GetEvtNr().at(i) == iEv)
		       if ( _ldata.GetBunchXID().at(i) == currentBunchXID )
			 {
			   // _ldata. PrintParameters(i);
			   
			   int iBxID = _ldata.GetBunchXID().at(i);
			   int iCyNr = _ldata.GetCycleNr().at(i);
			   int iCpID = _ldata.GetChipID().at(i);
			   int iAsNr = _ldata.GetASICNr().at(i);
			   int iEvNr = _ldata.GetEvtNr().at(i);
			   int iChan = _ldata.GetChannel().at(i);
			   int iTDC  = _ldata.GetTDC().at(i);
			   int iADC  = _ldata.GetADC().at(i);
			   //int iXPos = _ldata.GetXPos().at(i);
			   //int iYPox = _ldata.GetYPos().at(i);
			   int iHBit = _ldata.GetHitBit().at(i);
			   int iGBit = _ldata.GetGainBit().at(i);

			   unsigned int module = _HBUNumber;
			   unsigned int chip = static_cast<unsigned int>(iCpID); //(iCpID - 63);
			   unsigned int channel = static_cast<unsigned int>(iChan);
			   
			   int iXPos = static_cast<int>(e4dmapping.getIFromModuleChipChannel(module, chip, channel));
			   int iYPox = static_cast<int>(e4dmapping.getJFromModuleChipChannel(module, chip, channel));

			   LabviewBlock * lBlock = new LabviewBlock(iBxID, iCyNr, iCpID, iAsNr, iEvNr, iChan, iTDC, iADC, iXPos, iYPox, iHBit, iGBit);

			   _ldata.erase(i);
			   col->addElement( lBlock );


			 } // Add event element
		       //else { nextBunchXID =  _ldata.GetBunchXID().at(i); }
		       
		     } // complete one event
		     
		     //currentBunchXID = nextBunchXID;

		     evt->addCollection( col, "LabviewData" );

		     ProcessorMgr::instance()->processEvent( evt ) ;

		     // ------------ IMPORTANT ------------- !
		     // we created the event so we need to delete it ...
		     delete evt ;

		     
		   } //get next event in this cycle

		 _ldata.Clear();
		 m_out(DEBUG0) <<" After clear, _ldata.size(): "<<_ldata.GetBunchXID().size() <<std::endl;
		 CycleNrChanged = false;

	       } //Complete splite the event in this cycle

	 } // if the current Cycle is completed read in
       

       _ldata.SetLConverter(read);


    }

    file_to_read.close();

    delete runHdr ;
    
   }



  void LabviewConverter::end() {    
    m_out(MESSAGE) <<" ===== End of LabviewConverter processor ===== " << std::endl;
  }

} //namespace
